﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model;

namespace Interpreter
{
	public class StringInterpreter
	{

		private Dictionary<string, CellDefinition> zellen;
		public Dictionary<string, CellDefinition> Zellen
		{
			get { return zellen; }
			set { zellen = value; }
		}

		public List<CellDefinition> GetCellDefinitionAsList()
		{
			List<CellDefinition> cellDefs = new List<CellDefinition>();

			foreach (CellDefinition tmpCD in Zellen.Values)
			{
				Console.WriteLine(tmpCD);
				cellDefs.Add(tmpCD);
			}

			return cellDefs;
		}

		public StringInterpreter()
		{
			zellen = new Dictionary<string, CellDefinition>();
		}

		public void initializeEnvironment()
		{
			Model.Environment.initializeEnvironment(EnvironmentType.VonNeumann, zellen);
		}

		/**
		 * Erstellt eine Zellinstanz.
		 * 
		 * @param zellExpression
		 *            string aus dem die Zelle erstellt wird. Folgendes Format muss
		 *            dieser string haben: "[Zellname]
		 *            ([Attributname]:[Double|String],
		 *            [Attributname]:[Double|String], ... )
		 * @throws Exception
		 */
		public void initializeZelle(string zellExpression)
		{
			zellExpression = zellExpression.Replace(" ", "");
			String[] nameSeperiert = zellExpression.Split('(');
			string name = nameSeperiert[0];
			string attributListe = nameSeperiert[1].Replace(")", "");

			CellDefinition neueZelle = new CellDefinition(name);

			if (attributListe.Contains(','))
			{
				string[] stringAttribute = attributListe.Split(',');
				foreach (string tmpAttribut in stringAttribute)
				{
					String[] attributNameSeperiert = tmpAttribut.Split(':');
					string attributName = attributNameSeperiert[0];
					string attributString = attributNameSeperiert[1];

					if (attributString.Equals("string"))
					{
						neueZelle.addAttribut(new StringAttribut(attributName, ""));
					}
					else if (attributString.Equals("Double"))
					{
						neueZelle.addAttribut(new NumberAttribut(attributName, 0.0));
					}
					else
					{
						throw new Exception(attributString + " ist ein ungültiger Typ.");
					}
				}
			}

			zellen.Add(name, neueZelle);
		}

		public Expression evaluateRuleExpression(string expression)
		{

			int numberKlammerAuf = 0;
			int numberKlammerZu = 0;

			for (int i = 0; i < expression.Length; i++)
			{
				if (expression[i] == '(')
					numberKlammerAuf++;
				if (expression[i] == ')')
					numberKlammerZu++;
			}

			if (numberKlammerAuf != numberKlammerZu)
				throw new Exception("Ungültige Expression: " + numberKlammerAuf
						+ " mal (, " + numberKlammerZu + " mal ).");

			String[] nameSeperiert = expression.Split('(');
			string name = nameSeperiert[0];

			if (zellen[name] != null)
			{
				CellDefinition accordingCell = zellen[name];
				int nameLength = name.Length + 1;
				Expression returnExp = createExpression(expression.Substring(
						nameLength, expression.Length - 1 - nameLength), accordingCell);

				returnExp.CellDefinition = accordingCell;

				return returnExp;
			}
			else
			{
				throw new Exception("Zelle mit dem Namen: " + name + " wurde nicht initialisiert, ändern sie den Namen, oder initialisieren Sie eine solche Zelle!");
			}

		}

		private Expression createExpression(string stringExpression, CellDefinition accordingCell)
		{
			Expression neueExpression;

			List<String> geklammerterTeil = getKlammerPart(stringExpression);
			if (geklammerterTeil.Count != 0)
			{
				if (geklammerterTeil.Count != 2)
					throw new Exception(
							"Alle Expressions müssen so geklammert werden, dass jeder Klammerblock eine Unterexpression mit zwei oder keinen weiteren Klammerblöcken umschließt.");

				Expression leftSide = createExpression(geklammerterTeil[0], accordingCell);
				Expression rightSide = createExpression(geklammerterTeil[1], accordingCell);
				stringExpression = stringExpression.Replace(geklammerterTeil[0], "");
				stringExpression = stringExpression.Replace(geklammerterTeil[1], "");
				stringExpression = stringExpression.Replace(" ", "");
				stringExpression = stringExpression.Replace("(", "");
				stringExpression = stringExpression.Replace(")", "");
				string boolComp = stringExpression;

				neueExpression = new NestedExpression(leftSide, boolComp, rightSide);

			}
			else
			{

				string boolOp = "or";
				string[] boolSplittedExpr = stringExpression.Split(new string[] { " or " }, StringSplitOptions.None);
				if (boolSplittedExpr.Length == 1)
				{
					boolOp = "xor";
					boolSplittedExpr = stringExpression.Split(new string[] { " xor " }, StringSplitOptions.None);
				}

				if (boolSplittedExpr.Length == 1)
				{
					boolOp = "and";
					boolSplittedExpr = stringExpression.Split(new string[] { " and " }, StringSplitOptions.None);
				}

				if (boolSplittedExpr.Length == 1)
				{
					char op = ' ';
					if (stringExpression.Contains("="))
						op = '=';

					if (stringExpression.Contains("<"))
						op = '<';

					if (stringExpression.Contains(">"))
						op = '>';

					string[] boolSplittedSingleExpr = stringExpression.Split(op);
					neueExpression = new SingleExpression(boolSplittedSingleExpr[0], op.ToString(), boolSplittedSingleExpr[1]);
				}
				else
				{


					Expression leftSide = null;
					Expression rightSide = null;


					if (boolSplittedExpr.Length == 2)
					{
						leftSide = createExpression(boolSplittedExpr[0], accordingCell);
						rightSide = createExpression(boolSplittedExpr[1], accordingCell);
					}
					else
					{
						throw new Exception(stringExpression + " konnte nicht als Doppelte Expression ausgewertet werden.");
					}

					neueExpression = new NestedExpression(leftSide, boolOp, rightSide);
				}
			}
			neueExpression.CellDefinition = accordingCell;
			return neueExpression;
		}

		/**
		 * Sucht die Ausdrücke auf der nächstunterlegenden Klammerebene und gibt
		 * diese als String-Array zurück.
		 * 
		 * @param stringExpression
		 * @return
		 * @throws Exception
		 */
		private List<String> getKlammerPart(string stringExpression)
		{
			List<String> embeddedExpressions = new List<String>();

			int klammerCounter = 0;

			bool searchingFirstKlammerAuf = true;
			int index = 0;
			for (int i = 0; i < stringExpression.Length; i++)
			{
				if (stringExpression[i] == '(')
				{
					klammerCounter++;
					// hier ist die erste Klammer auf
					if (klammerCounter == 1)
					{
						index = i + 1;
						searchingFirstKlammerAuf = false;
					}
				}

				if (stringExpression[i] == ')')
				{
					klammerCounter--;
				}

				if (klammerCounter == 0 && !searchingFirstKlammerAuf)
				{
					string embeddedExpression = stringExpression
							.Substring(index, i - index);
					embeddedExpressions.Add(embeddedExpression);
					searchingFirstKlammerAuf = true;
				}
			}

			if (klammerCounter != 0)
				throw new Exception(
						"Ausgewertete Expression ist ungültig (Klammern): "
								+ stringExpression);

			return embeddedExpressions;
		}

	}
}
